//#include <gtkmm.h>

#include <iostream>
#include <sstream>
#include "../utility/assert_helper.h"
#include "../utility/charset_convert.h"
#include "../hive_access/nt_hive.h"
#include "../windows_manager/windows_manager.h"
//#include "../windows_manager/sys_search.h"
#include "../windows_manager/reg_accessor.h"
#include "regsz_editor.h"
#include "regdword_editor.h"
#include "main_form.h"

extern const char *HKEY_ROOTS[HKEY_INVALID_ROOT];

Main_Form::Main_Form() : 
right_menu_popup(NULL), validate_retry_(false)
{
	I_Windows_Manager * win_manager = ctreate_windows_manager();
	if (NULL != win_manager)
	if (0 == win_manager->get_windows_total()) {
		Gtk::Dialog no_system;
		no_system.run();
	}
	reg_ = new Hive_Accessor(win_manager->get_windows_information(0)); // dynamic get
	set_default_size(640, 480);
	set_title("regedit");

	context_id_ = status_bar_.get_context_id("regedit status bar");

		add(vbox_);
		add_menu_bar();
		Gtk::HPaned * paned = Gtk::manage(new Gtk::HPaned);
		if (NULL != paned) {
			paned->set_position(200);
			vbox_.pack_start(*paned);			
			Gtk::ScrolledWindow * sw1 = 
			Gtk::manage(new Gtk::ScrolledWindow());
			Gtk::ScrolledWindow * sw2 = 
			Gtk::manage(new Gtk::ScrolledWindow());
			create_popup_menu();
			if (NULL != sw1) {				
				paned->add1(*sw1);
				sw1->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
				sw1->add(key_tree_);
				key_model_ = Gtk::TreeStore::create(tree_columns_);
				key_tree_.set_model(key_model_); // bind model to view
				key_tree_.append_column("Path", tree_columns_.path_);
				key_model_initialize();
				key_tree_.signal_row_expanded().connect(sigc::mem_fun(*this,
				&Main_Form::on_tree_row_expanded));
				key_tree_.signal_cursor_changed().connect(sigc::mem_fun(*this,
				&Main_Form::on_change_cursor));
				key_tree_.delete_key_selected_.connect(
				sigc::mem_fun(*this, &Main_Form::on_key_delete));
				key_tree_.key_whole_path_copy_.connect(
				sigc::mem_fun(*this, &Main_Form::on_key_whole_path_copy));
				key_tree_.key_path_exported_.connect(
				sigc::mem_fun(*this, &Main_Form::on_key_export));
			}
			if (NULL != sw2) {
				paned->add2(*sw2);
				sw2->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
				sw2->add(val_view_);
				val_model_ = Gtk::ListStore::create(list_columns_);
				val_view_.set_model(val_model_);
				treeviewcolumn_validated_.set_title("Name");
				treeviewcolumn_validated_.pack_start(cellrenderer_validated_);				
				val_view_.append_column(treeviewcolumn_validated_);
				val_view_.append_column("Type", list_columns_.type_);
				val_view_.append_column("Data", list_columns_.data_);
				
				val_view_.signal_row_activated().connect(sigc::mem_fun(*this,
				&Main_Form::on_view_dblclick));
				val_view_.edit_selected_.connect(sigc::mem_fun(*this, &Main_Form::on_view_dblclick));
				val_view_.delete_selected_.connect(sigc::mem_fun(*this, &Main_Form::on_value_delete));

				//Tell the view column how to render the model values:
				treeviewcolumn_validated_.set_cell_data_func(cellrenderer_validated_,
				sigc::mem_fun(*this, &Main_Form::treeviewcolumn_validated_on_cell_data));

				//Make the CellRenderer editable, and handle its editing signals:
				#ifdef GLIBMM_PROPERTIES_ENABLED
				  cellrenderer_validated_.property_editable() = true;
				#else
				  cellrenderer_validated_.set_property("editable", true);
				#endif

				cellrenderer_validated_.signal_editing_started().connect(
					sigc::mem_fun(*this,
					&Main_Form::cellrenderer_validated_on_editing_started) );

				cellrenderer_validated_.signal_edited().connect( sigc::mem_fun(*this,
					&Main_Form::cellrenderer_validated_on_edited) );
			}
		}
		vbox_.pack_start(status_bar_, Gtk::PACK_SHRINK);
		status_bar_.push("start", context_id_);

	show_all_children();
}

Main_Form::~Main_Form()
{
	delete reg_;
}

int Main_Form::add_menu_bar()
{
	if (action_group_ || ui_manager_) return -1;
  //Create actions for menus and toolbars:
  action_group_ = Gtk::ActionGroup::create();

  //File|New sub menu:
  action_group_->add(Gtk::Action::create("FileNewStandard",
              Gtk::Stock::NEW, "_New", "Create a new file"));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_file_new_generic)0);*/

  action_group_->add(Gtk::Action::create("FileNewFoo",
              Gtk::Stock::NEW, "New Foo", "Create a new foo"));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_file_new_generic)0);*/

  action_group_->add(Gtk::Action::create("FileNewGoo",
              Gtk::Stock::NEW, "_New Goo", "Create a new goo"));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_file_new_generic)0);*/

  //File menu:
  action_group_->add(Gtk::Action::create("FileMenu", "File"));
  //Sub-menu.
  action_group_->add(Gtk::Action::create("FileNew", Gtk::Stock::NEW));
  action_group_->add(Gtk::Action::create("FileQuit", Gtk::Stock::QUIT));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_file_quit)0);*/

  //Edit menu:
  action_group_->add(Gtk::Action::create("EditMenu", "Edit"));
  action_group_->add(Gtk::Action::create("EditCopy", Gtk::Stock::COPY));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_others)0);*/
  action_group_->add(Gtk::Action::create("EditPaste", Gtk::Stock::PASTE));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_others)0);*/
  action_group_->add(Gtk::Action::create("EditSomething", "Something"));
          /*Gtk::AccelKey("<control><alt>S"),
          sigc::mem_fun(*this, &Main_Form::on_menu_others)0);*/

  //Help menu:
  action_group_->add( Gtk::Action::create("HelpMenu", "Help") );
  action_group_->add( Gtk::Action::create("HelpAbout", Gtk::Stock::HELP));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_others)0 );*/

  ui_manager_ = Gtk::UIManager::create();
  ui_manager_->insert_action_group(action_group_);

  add_accel_group(ui_manager_->get_accel_group());

  //Layout the actions in a menubar and toolbar:
  Glib::ustring ui_info = 
        "<ui>"
        "  <menubar name='MenuBar'>"
        "    <menu action='FileMenu'>"
        "      <menu action='FileNew'>"
        "        <menuitem action='FileNewStandard'/>"
        "        <menuitem action='FileNewFoo'/>"
        "        <menuitem action='FileNewGoo'/>"
        "      </menu>"
        "      <separator/>"
        "      <menuitem action='FileQuit'/>"
        "    </menu>"
        "    <menu action='EditMenu'>"
        "      <menuitem action='EditCopy'/>"
        "      <menuitem action='EditPaste'/>"
        "      <menuitem action='EditSomething'/>"
        "    </menu>"
        "    <menu action='HelpMenu'>"
        "      <menuitem action='HelpAbout'/>"
        "    </menu>"
        "  </menubar>"
        "</ui>";

  #ifdef GLIBMM_EXCEPTIONS_ENABLED
  try
  {
    ui_manager_->add_ui_from_string(ui_info);
  }
  catch(const Glib::Error& ex)
  {
    std::cerr << "building menus failed: " <<  ex.what();
  }
  #else
  std::auto_ptr<Glib::Error> ex;
  ui_manager_->add_ui_from_string(ui_info, ex);
  if(ex.get())
  {
    std::cerr << "building menus failed: " <<  ex->what();
    return -1;
  }
  #endif //GLIBMM_EXCEPTIONS_ENABLED

  //Get the menubar and toolbar widgets, and add them to a container widget:
  Gtk::Widget* pMenubar = ui_manager_->get_widget("/MenuBar");
  if(pMenubar)
    vbox_.pack_start(*pMenubar, Gtk::PACK_SHRINK);
	return 0;
}

int
Main_Form::create_popup_menu()
{
	if (left_popup_action_group_ ||
	left_popup_ui_manager_) return -1;
	left_popup_action_group_ = Gtk::ActionGroup::create();
	left_popup_action_group_->add(Gtk::Action::create("FileNewStandard",
              Gtk::Stock::NEW, "_New", "Create a new file"));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_file_new_generic)0);*/

	left_popup_action_group_->add(Gtk::Action::create("FileNewFoo",
              Gtk::Stock::NEW, "New Foo", "Create a new foo"));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_file_new_generic)0);*/

	left_popup_action_group_->add(Gtk::Action::create("FileNewGoo",
              Gtk::Stock::NEW, "_New Goo", "Create a new goo"));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_file_new_generic)0);*/
         
	if (right_popup_action_group_ ||
	right_popup_ui_manager_) return -1;
	right_popup_action_group_ = Gtk::ActionGroup::create();
	right_popup_action_group_->add(Gtk::Action::create("FileNewStandard",
              Gtk::Stock::NEW, "_New", "Create a new file"));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_file_new_generic)0);*/

	right_popup_action_group_->add(Gtk::Action::create("FileNewFoo",
              Gtk::Stock::NEW, "New Foo", "Create a new foo"));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_file_new_generic)0);*/

	right_popup_action_group_->add(Gtk::Action::create("FileNewGoo",
              Gtk::Stock::NEW, "_New Goo", "Create a new goo"));
          /*sigc::mem_fun(*this, &Main_Form::on_menu_file_new_generic)0);*/         

  right_popup_ui_manager_ = Gtk::UIManager::create();
  right_popup_ui_manager_->insert_action_group(right_popup_action_group_);

  add_accel_group(ui_manager_->get_accel_group());

  //Layout the actions in a menubar and toolbar:
  Glib::ustring right_popup_ui_info = 
        "<ui>"
        "  <popup name='PopupMenu'>"
        "    <menuitem action='FileNewStandard'/>"
        "    <menuitem action='FileNewFoo'/>"
        "    <menuitem action='FileNewGoo'/>"
        "  </popup>"
        "</ui>";

  #ifdef GLIBMM_EXCEPTIONS_ENABLED
  try
  {
    right_popup_ui_manager_->add_ui_from_string(right_popup_ui_info);
  }
  catch(const Glib::Error& ex)
  {
    std::cerr << "building menus failed: " <<  ex.what();
  }
  #else
  std::auto_ptr<Glib::Error> ex;
  right_popup_ui_manager_->add_ui_from_string(right_popup_ui_info, ex);
  if(ex.get())
  {
    std::cerr << "building menus failed: " <<  ex->what();
  }
  #endif //GLIBMM_EXCEPTIONS_ENABLED

  //Get the menu:
	if (NULL == right_menu_popup) {
	right_menu_popup = dynamic_cast<Gtk::Menu*>(
		right_popup_ui_manager_->get_widget("/PopupMenu")); 
	if(!right_menu_popup)
		g_warning("menu not found");  	
  	}

	return 0;
}

void 
Main_Form::key_model_initialize()
{
	Gtk::TreeModel::Row row;
	
	for (int i = 0; i < HKEY_INVALID_ROOT; ++i) {
		row = *(key_model_->append());
		row[tree_columns_.path_] = HKEY_ROOTS[i];
		Gtk::TreeModel::Row placeholder = *(key_model_->append(row.children()));
		placeholder[tree_columns_.path_] = "\\"; // 加入占位子键
	}
}

// 键树展开回调函数
void 
Main_Form::on_tree_row_expanded(const Gtk::TreeModel::iterator & iter, 
const Gtk::TreeModel::Path & path)
{
	Glib::ustring level = key_model_->get_string(iter);
	if (Glib::ustring::npos == level.find(":")) { // is a root key
		int index = atoi(level.c_str());
		//Gtk::TreeModel::Children children = key_model_->children();
		Gtk::TreeModel::Children child = (*iter).children();
		// 判断是否有子键
		if (!child.empty()) {
			Glib::ustring val = child.begin()->get_value(tree_columns_.path_);
			printf("%s\n", val.c_str());
			if ("\\" != val) {
				return;
			}
		}
		if (index >= 0 && index < HKEY_INVALID_ROOT) {
			const Hive_Root & hroot = reg_->take_root(static_cast<HKEY>(index));
			Hive_Root::Link_Relations::const_iterator end = hroot.link_relations().end();
			for (Hive_Root::Link_Relations::const_iterator begin = hroot.link_relations().begin();
			begin != end; ++begin) {
				g_print("%s->%s.\n", begin->key_name().c_str(), begin->target().c_str());
				// 增加子键
				if (!begin->key_name().empty()) // 命名子键
				{
					path_split_for_tree_store(iter, begin->key_name());
					/*Gtk::TreeModel::Row child = *(key_model_->append((*iter).children()));
					Gtk::TreeModel::Row grandson = *(key_model_->append(child.children()));
					size_t pos = begin->key_name().find_first_of("\\");
					if (std::string::npos != pos) { // 映射键，被拆分
						child[tree_columns_.path_] = begin->key_name().substr(0, pos);
						grandson[tree_columns_.path_] = begin->key_name().substr(pos + 1);
					} else {
						child[tree_columns_.path_] = begin->key_name().c_str();
						grandson[tree_columns_.path_] = "\\";	//加入占位子键（之后会被真实子键替换）					
					}*/
				} else {  //匿名子键（映射键）
					size_t delimiter = begin->target().find_first_of("\\");
					if (std::string::npos != delimiter) {
						reg_->reg_open(begin->target().substr(0, delimiter).c_str(),
						begin->target().substr(delimiter + 1).c_str());
						if (reg_->is_available()) {
							select_iter_ = iter;
							reg_->subkey_foreach("./", travel_path_for_show_all_subkeys, static_cast<void *>(this));
						}							
					} else {
						g_print("%s may be a invalid key name\n", begin->target().c_str());
					}
				}
			}
			if ("\\" == (*(*iter).children().begin())[tree_columns_.path_])
			key_model_->erase((*iter).children().begin()); // 删除首子键（\）
		}
	} else { // a child key
		Glib::ustring key_path;
		Glib::ustring path_str = level;
		size_t delimiter = Glib::ustring::npos;
		do {
				path_str = path_str.substr(0, delimiter);
				Gtk::TreeIter child = key_model_->get_iter(path_str);
				//gtk_tree_model_get_iter_from_string((GtkTreeModel *)data->tree_model_,
				//&child, path_str.c_str());
				//gtk_tree_model_get((GtkTreeModel *)data->tree_model_, &child, 0, &cname, -1);
				// 通过判断去除最后的\符
				if (!key_path.empty()) {
					key_path = (*child)[tree_columns_.path_] + Glib::ustring("\\") + key_path;
				} else {
					key_path += (*child)[tree_columns_.path_];
				}					
				delimiter = path_str.find_last_of(":");
			} while (Glib::ustring::npos != delimiter);
		Glib::ustring cname;
		Gtk::TreeModel::Children tmp_child = (*iter).children();
		if (!(tmp_child.empty())) {
			//gtk_tree_model_get((GtkTreeModel *)data->tree_model_, &tmp_iter, 0, &cname, -1);
			cname = (*tmp_child.begin()).get_value(tree_columns_.path_);
			g_print("son: %s\n", cname.c_str());
			if (1 == cname.size() &&'\\' == cname[0]) { // 第一次展开子键
				g_print("first expend sub key\n");
				//gtk_tree_model_iter_nth_child((GtkTreeModel *)data->tree_model_, &child, &tmp_iter, 0);
				size_t delimiter = key_path.find_first_of("\\");
				if (Glib::ustring::npos != delimiter) {
					reg_->reg_open(key_path.substr(0, delimiter).c_str(),
					key_path.substr(delimiter + 1).c_str());
					g_print("|>%s\n", key_path.c_str());
					if (reg_->is_available()) {							
						select_iter_ = iter;
						reg_->subkey_foreach("./", travel_path_for_show_all_subkeys, static_cast<void *>(this));
						key_model_->erase(tmp_child.begin());
						//gtk_tree_store_remove((GtkTreeStore *)data->tree_model_, &tmp_iter); // 删除首子键（\）
					}
				}
			}
		}			
	}		
}

void	
Main_Form::path_split_for_tree_store(const Gtk::TreeModel::iterator & iter, const Glib::ustring & path)
{
	size_t pos = 0;
	bool	is_only = true;
	Glib::ustring	prefix;
	Glib::ustring	last = path;
	Gtk::TreeModel::iterator beg, end;
	Gtk::TreeModel::Row child_row = (*iter);
	do {
		pos = last.find_first_of("\\");
		prefix = last.substr(0, pos);
		if (Glib::ustring::npos != pos) last = last.substr(pos + 1);
		end = child_row.children().end();
		for (beg = child_row.children().begin(); beg != end; ++beg) {
			if (prefix == (*beg)[tree_columns_.path_]) {				
				is_only = false;
				break;
			}
		}
		if (is_only) {
			child_row = *(key_model_->append(child_row.children()));			
			child_row[tree_columns_.path_] = prefix;
			//printf("^%s\n", prefix.c_str());
		} else {
			child_row = *(key_model_->prepend((*beg).children()));	
			child_row[tree_columns_.path_] = "\\";
			is_only = true;
		}
	} while (Glib::ustring::npos != pos);
	child_row = *(key_model_->append(child_row.children()));
	child_row[tree_columns_.path_] = "\\";
}

Glib::ustring 
Main_Form::get_whole_key_name_from_path_string(Glib::ustring path_str) const
{
	Glib::ustring key_path;
	Glib::ustring str;
	size_t delimiter = Glib::ustring::npos;
	do {
			path_str = path_str.substr(0, delimiter);
			Gtk::TreeModel::iterator iter = key_model_->get_iter(path_str);
			str = (*iter)[tree_columns_.path_];
			//gtk_tree_model_get_iter_from_string(tree_model, iter, path_str.c_str());
			//gtk_tree_model_get(tree_model, iter, 0, &cname, -1);
			//gtk_tree_model_get((GtkTreeModel *)data->tree_model_, &child, 0, &cname, -1);
			if (!str.empty()) {
				// 通过判断去除最后的\符
				if (!key_path.empty()) {
					key_path = str + Glib::ustring("\\") + key_path;
				} else {
					key_path += str;
				}					
			}
			delimiter = path_str.find_last_of(":");
		} while (Glib::ustring::npos != delimiter);
	return key_path;	
}

void 
Main_Form::on_change_cursor()
{
	Gtk::TreeModel::Path gpath;
	Gtk::TreeViewColumn * column = NULL;
	key_tree_.get_cursor(gpath, column);
	Glib::ustring path = get_whole_key_name_from_path_string(gpath.to_string());
	Glib::ustring root, subkey;
	if (reg_relation_analyzer(path, root, subkey)){
		if (0 == reg_->reg_open(root.c_str(), subkey.c_str())) {
			val_model_->clear();
			current_key_path_ = path;
			reg_->value_foreach("./", travel_key_for_show_all_values, this);
		} else {
			printf("open %s\\%s failed!!\n", root.c_str(), subkey.c_str());
		}
	} else {
		current_key_path_ = path;
	}
	status_bar_.pop(context_id_);
	status_bar_.push(current_key_path_, context_id_);
}

bool
Main_Form::reg_relation_analyzer(const Glib::ustring & whole_path, Glib::ustring & root, 
Glib::ustring & subkey) const
{
	if (!whole_path.empty()) {
		size_t delimiter = whole_path.find_first_of("\\");
		if (Glib::ustring::npos != delimiter) {
			root = whole_path.substr(0, delimiter);
			subkey = whole_path.substr(delimiter + 1);
			return true;
		}
	}
	return false;
}

void
Main_Form::on_view_dblclick(const Gtk::TreeModel::Path & path, Gtk::TreeViewColumn * column)
{
	if (NULL == reg_) return ;
	Gtk::TreeModel::iterator iter = val_model_->get_iter(path.to_string());
	Glib::ustring name = (*iter)[list_columns_.name_];
	Glib::ustring root;
	Glib::ustring subkey;
	std::stringstream text;
	if (reg_relation_analyzer(current_key_path_, root, subkey))	
		reg_->reg_open(root.c_str(), subkey.c_str());
	if (!reg_->is_available()) return;
	if ("REG_SZ" == (*iter)[list_columns_.type_] ||
		 "REG_EXPAND_SZ" == (*iter)[list_columns_.type_]) { // string in little entian
		Regsz_Editor editor(name, (*iter)[list_columns_.data_]);
		if (editor.run() == Gtk::RESPONSE_OK && editor.is_modify()) {
			(*iter)[list_columns_.data_] = editor.get_result();
			Glib::ustring data = (*iter)[list_columns_.data_];
			size_t size = utility::UTF_8ToUnicode(NULL, 0, data.c_str());
			if (utility::CONVERT_ERROR != size) {				
				utility::TCHAR * unicode = new utility::TCHAR [size + 1];
				if (NULL != unicode) {
					utility::UTF_8ToUnicode(unicode, (size + 1) * sizeof(utility::TCHAR), 
					data.c_str());
					reg_->set_value(name.c_str(), unicode,	(size + 1/* with '\0' */) * 
					sizeof(utility::TCHAR));
					printf("set register %s length %u\n", data.c_str(), size + 1);
					delete [] unicode;
				} else {printf("alloc memory failed.\n");}
			} else {printf("value data length is invalid.\n");}
		} else {printf("value is not modifed.\n");}
	} else if ("REG_DWORD" == (*iter)[list_columns_.type_]) { // dword in little entian
		unsigned int val = 0;
		if (reg_->get_value(name.c_str(), val)) {
			Regdword_Editor editor(name, val);
			if (editor.run() == Gtk::RESPONSE_OK && editor.is_modify()) {
				val = editor.get_result();
				reg_->set_value(name.c_str(), &val,	sizeof(Hive_Accessor::DWORD), REG_DWORD);
				text << std::hex;
				text << "0x" << val;
				text << std::dec;
				text << " (" << val << ")";
				(*iter)[list_columns_.data_] = text.str();
			}
		}
	} else if ("REG_MULTI_SZ" == (*iter)[list_columns_.type_]) {
		Ustrings multi_strings;
		Regmultisz_Editor editor(name, multi_strings);
			if (editor.run() == Gtk::RESPONSE_OK && editor.is_modified()) {

			}
	}
}


bool 
Main_Form::on_button_release_event(GdkEventButton* event)
{
    return Gtk::Window::on_button_release_event(event);
}


void
travel_path_for_show_all_subkeys(const char *path, struct nk_key * key_data, void *userdata)
{
	g_return_if_fail(NULL != path && NULL != key_data && NULL != userdata);
	Main_Form *main_form = reinterpret_cast<Main_Form *>(userdata);
	Gtk::TreeModel::iterator iter = main_form->select_iter_;

	Gtk::TreeModel::Row row = *(main_form->key_model_->append((*iter).children()));
	row[main_form->tree_columns_.path_] = path;
	//g_print("[%s]\n", path);
	/*gtk_tree_store_append((GtkTreeStore*)(*data->tree_model_), &child, *data->select_iter_);
	gtk_tree_store_set((GtkTreeStore*)(*data->tree_model_), &child, COLUMN, path, -1);*/
	if (0 < key_data->no_subkeys) {
		Gtk::TreeModel::Row child = *(main_form->key_model_->append(row.children()));
		child[main_form->tree_columns_.path_] = "\\";
	}
}

void	
Main_Form::on_value_delete(const Gtk::TreeModel::iterator & iter)
{
	Glib::ustring name = (*iter)[list_columns_.name_];
	Glib::ustring root;
	Glib::ustring subkey;
	std::stringstream text;
	if (reg_relation_analyzer(current_key_path_, root, subkey))	
		reg_->reg_open(root.c_str(), subkey.c_str());
	if (!reg_->is_available()) return;
	if (0 == reg_->delete_value(name.c_str())) {	// register delete successful.
		if (val_model_) val_model_->erase(iter);
	}
}

void
Main_Form::on_key_delete(const Gtk::TreeModel::iterator & iter)
{
	Glib::ustring root, path;
	if (reg_relation_analyzer(current_key_path_, root, path)) {
		reg_->reg_open(root.c_str(), path.substr(0, path.find_last_of("\\")).c_str());
		if (0 == reg_->remove_allkeys(path.substr(path.find_last_of("\\") + 1).c_str())) {
			if (key_model_) key_model_->erase(iter);
		}		
	}
}

void	
Main_Form::treeviewcolumn_validated_on_cell_data(Gtk::CellRenderer* ,
const Gtk::TreeModel::iterator& iter)
{
  //Get the value from the model and show it appropriately in the view:
  if(iter)
  {
    Gtk::TreeModel::Row row = *iter;
    Glib::ustring view_text = row[list_columns_.name_];

    //This is just an example.
    //In this case, it would be easier to use append_column_editable() or
    //append_column_numeric_editable()
    /*char buffer[32];
    sprintf(buffer, "%s", model_value); 

    Glib::ustring view_text = buffer;*/
#ifdef GLIBMM_PROPERTIES_ENABLED
    cellrenderer_validated_.property_text() = view_text;
#else
    cellrenderer_validated_.set_property("text", view_text);
#endif
  }
}

void Main_Form::cellrenderer_validated_on_editing_started(
        Gtk::CellEditable* cell_editable, const Glib::ustring& /* path */)
{
  //Start editing with previously-entered (but invalid) text, 
  //if we are allowing the user to correct some invalid data. 
  if(validate_retry_)
  {
    //This is the CellEditable inside the CellRenderer. 
    Gtk::CellEditable* celleditable_validated = cell_editable;

    //It's usually an Entry, at least for a CellRendererText:
    Gtk::Entry* pEntry = dynamic_cast<Gtk::Entry*>(celleditable_validated);
    if(pEntry)
    {
      pEntry->set_text(invalid_text_for_retry_);
      validate_retry_ = false;
      invalid_text_for_retry_.clear();
    }
  }
}

void Main_Form::cellrenderer_validated_on_edited(
        const Glib::ustring& path_string,
        const Glib::ustring& new_text)
{
  Gtk::TreePath path(path_string);

    //Get the row from the path:
	Gtk::TreeModel::iterator iter = val_model_->get_iter(path);
	if(iter)
	{
		Gtk::TreeModel::Row row = *iter;
		if (reg_->is_available()) {
			Glib::ustring old = row[list_columns_.name_];
			reg_->value_rename(old.c_str(), new_text.c_str());
			//printf("old = %s, new = %s\n", old.c_str(), new_text.c_str());
			//Put the new value in the model:
			row[list_columns_.name_] = Glib::ustring(new_text.c_str());
			
		}
		
	}
}

void
Main_Form::on_key_whole_path_copy(const Gtk::TreeModel::iterator & iter)
{
  Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
  refClipboard->set_text(current_key_path_);	
}

void
Main_Form::on_key_export(const Gtk::TreeModel::iterator & iter)
{
	Gtk::FileChooserDialog dialog("Please choose a file",
			 Gtk::FILE_CHOOSER_ACTION_SAVE);
	dialog.set_transient_for(*this);

	//Add response buttons the the dialog:
	dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
	dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);

	//Add filters, so that only certain file types can be selected:

	/*Gtk::FileFilter filter_text;
	filter_text.set_name(".Reg file");
	filter_text.add_mime_type("text/plain");
	dialog.add_filter(filter_text);*/

	Gtk::FileFilter filter_any;
	filter_any.set_name(".reg file");
	filter_any.add_pattern("*");
	dialog.add_filter(filter_any);

	//Show the dialog and wait for a user response:
	int result = dialog.run();

	//Handle the response:
	switch(result)
	{
		case(Gtk::RESPONSE_OK):
		{
			//std::cout << "Open clicked." << std::endl;
			//Notice that this is a std::string, not a Glib::ustring.
			std::string filename = dialog.get_filename();
			std::cout << "File selected: " <<  filename << std::endl;
			if (NULL != reg_) {
				reg_->key_export("./", filename.c_str(), 
				current_key_path_.substr(0, current_key_path_.find_last_of("\\")).c_str());
			}			
			break;
		}
		case(Gtk::RESPONSE_CANCEL):
		{
			//std::cout << "Cancel clicked." << std::endl;
			break;
		}
		default:
		{
			//std::cout << "Unexpected button clicked." << std::endl;
			break;
		}
	}
}

void
travel_key_for_show_all_values(const char * name, struct vk_key * val_data, void * userdata)
{
	g_return_if_fail(NULL != name && NULL != val_data && NULL != userdata);
	Main_Form *main_form = (Main_Form *)userdata;
	if (NULL != main_form->reg_) {	
		Gtk::ListStore::Row row = *(main_form->val_model_->append());
		row[main_form->list_columns_.name_] = name;
		row[main_form->list_columns_.type_] = Hive_Accessor::get_type_string(val_data->val_type);
		row[main_form->list_columns_.data_] = get_data_in_string(main_form->reg_, name);
	}
}

std::string
get_data_in_string(const Hive_Accessor *reg, const std::string & name)
{
	IF_NULL_RETURN_VALUE(reg, "");
	int	type = REG_NONE;
	std::stringstream	result;
	char	*buf = NULL;
	int len = reg->get_value_length(name.c_str());
	if (0 < len) {
		//printf("value len : %d\n", len);
		buf = new char[len];
		if (NULL != buf && 
		0 == reg->get_value(name.c_str(), buf, len, type)) {
			switch (type) {
				case	REG_DWORD:	// output like "0x80 (128)"
				{
					result << std::hex;
					result << "0x" << *(reinterpret_cast<unsigned int *>(buf));
					result << std::dec;
					result << " (" << *(reinterpret_cast<unsigned int *>(buf)) << ")";
				}
				break;
				case	REG_SZ:	// output as string
				case	REG_EXPAND_SZ:
				{
					size_t buf_len = len / 2 * 3;
					char * str = new char [buf_len]; // alloc 3 multiple than characters count in unicode
					if (NULL != str) {
						utility::UnicodeToUTF_8(str, buf_len - 1, reinterpret_cast<utility::TCHAR *>(buf));
						result << str;
						delete [] str;			
					}
				}
				break;
				case	REG_MULTI_SZ:
				{
					char str[8] = {0}; // alloc 3 multiple than characters count in unicode
					if (NULL != str && 0 == (len & 1)) { // len为偶数才解析
						utility::TCHAR tmp[2] = {0};
						tmp[1] = 0;
						for (int i = 0; i < len; i += 2) {
							tmp[0] = *(reinterpret_cast<utility::TCHAR *>(buf + i));
							if (0 != tmp[0]) {
								memset(str, 0, 8);
								utility::UnicodeToUTF_8(str, 7, 
								reinterpret_cast<utility::TCHAR *>(tmp));								
							} else if (0 != *(reinterpret_cast<utility::TCHAR *>(buf + i + 2))) { // 前面是一个字符串
								tmp[0] = 0x20;	// instead to a space
								utility::UnicodeToUTF_8(str, 7, 
								reinterpret_cast<utility::TCHAR *>(tmp));									
							} else { // 所有字符串的结束
								break; // 停止解析
							}
							result << str;
						}			
					} else {
							// memory alloc failed or length of multistring is not even number
					}					
				}
				break;
				case	REG_BINARY:
				case	REG_RESOURCE_LIST:
				{
				}
				break;			
			}
			delete [] buf;
		}
	}

	return result.str();
}
